﻿using System;

namespace InDepth2Demo.Chapter5
{
    /// <summary>
    /// 5.4 在匿名方法中捕捉变量
    /// </summary>
    public class CapturingVariable
    {
        /// <summary>
        /// 闭包
        /// </summary>
        static void EnclosingMethod()
        {
            int outerVariable = 5;                             //外部变量（未捕获的变量）
            string captureVariable = "captured";               //外部变量（被匿名方法捕获的变量）
            if (DateTime.Now.Hour == 23)
            {
                int normalLocalVariable = DateTime.Now.Minute; //普通方法的局部变量
                Console.WriteLine(normalLocalVariable);
            }
            MethodInvoker x = delegate ()
            {
                string anonLocal = "local to anonymous method"; //匿名方法的局部变量  (只有委托实例被调用之后，它才会存在[于一个正在执行的栈帧(fame)]中。)
                Console.WriteLine(captureVariable + anonLocal); //捕获外部变量   (captureVariable是一个外部变量，因为在它的作用域内声明了一个匿名方法，且在这个位置使用了该变量，所以它成为了一个被捕捉的变量)
            };
            x();
        }

        /// <summary>
        /// 测试被捕获变量的行为
        /// </summary>
        public static void TestCaputuredVariable()
        {
            string captured = "before x is created";

            MethodInvoker x = delegate
            {
                Console.WriteLine(captured);
                captured = "changed by x";
            };

            captured = "directly before x is invoked";
            x();

            Console.WriteLine(captured);

            captured = "before second invocation";
            x();

            // 输出
            // directly before x is invoked
            // changed by x
            // before second invocation
        }

        /// <summary>
        /// 捕捉变量的生存期延长了
        /// </summary>
        /// <returns></returns>
        static MethodInvoker CreateDelegateInstance()
        {
            int counter = 5;
            MethodInvoker ret = delegate
            {
                Console.WriteLine(counter);
                counter++;
            };
            ret();
            return ret;
        }

        /// <summary> 
        /// 捕捉变量的生存期延长了 Call
        /// </summary>
        public static void Call()
        {
            MethodInvoker x = CreateDelegateInstance();  //执行CreateDelegateInstance方法，在ret()时执行 ret 委托,执行完counter=6
            x();    //执行 ret 委托 ,执行完counter=7
            x();    //执行 ret 委托

            // 输出
            // 5
            // 6
            // 7
        }

        /// <summary>
        /// 捕捉不同作用域中的变量。
        /// </summary>
        public static void TestMixVariables()
        {
            MethodInvoker[] delegates = new MethodInvoker[2];

            int outside = 0;//被两个委托实例共享

            for (int i = 0; i < 2; i++)
            {
                int inside = 0;//每次都被实例化
                delegates[i] = delegate
                {
                    Console.WriteLine("{0},{1}", outside, inside);
                    outside++;
                    inside++;
                };
            }
            MethodInvoker first = delegates[0];
            MethodInvoker second = delegates[1];

            first();//0,0
            first();//1,1
            first();//2,2

            second();//3,0
            second();//4,1
        }
    }

    public delegate void MethodInvoker();
}
